﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MiniCPU.Events;

namespace MiniCPU.Entities
{
    /// <summary>
    /// Data Holder class.
    /// </summary>
    public class DataHolder : LimitedData
    {
        #region Variables
        /// <summary>
        /// Output Buffer to prevent change from reflecting immediately.
        /// </summary>
        private LimitedData outputBuffer;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DataHolder"/> class.
        /// </summary>
        /// <param name="name">The name of the data holder.</param>
        /// <param name="value">The value of the data holder.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public DataHolder(string name, Int32 value, int numberOfBits)
            : base(name, value, numberOfBits)
        {
            Clock.ClockTicked += new Clock.ClockPulseHandler(this.Clock_ClockTicked);
            Clock.ClockPropagated += new Clock.ClockPropagationHandler(this.Clock_ClockPropagated);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataHolder"/> class.
        /// </summary>
        /// <param name="name">The name of the data holder.</param>
        /// <param name="value">The value of the data holder.</param>
        /// <param name="valueBase">The value base.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public DataHolder(string name, string value, int valueBase, int numberOfBits) :
            this(name, Convert.ToInt32(value, valueBase), numberOfBits)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataHolder"/> class.
        /// </summary>
        /// <param name="name">The name of the data holder.</param>
        /// <param name="value">The value of the data holder.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public DataHolder(string name, byte value, int numberOfBits) :
            this(name, Convert.ToInt32(value), numberOfBits)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataHolder"/> class.
        /// </summary>
        /// <param name="name">The name of the data holder.</param>
        /// <param name="value">if set to <c>true</c> [value].</param>
        public DataHolder(string name, bool value)
            : this(name, Convert.ToInt32(value), 1)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataHolder"/> class.
        /// </summary>
        /// <param name="name">The name of the data holder.</param>
        /// <param name="value">The value of the data holder.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public DataHolder(string name, byte[] value, int numberOfBits) :
            this(name, GetIntFromByteArray(value), numberOfBits)
        {
        }
        #endregion

        #region Methods
        /// <summary>
        /// The clock propagated event handler.
        /// </summary>
        protected void Clock_ClockPropagated()
        {
            base.UpdateValue(this.outputBuffer.IntValue);
            if (IsDataChanged)
            {
                ExecuteEventHandlers();
            }
        }

        /// <summary>
        /// Clock ticked event handler.
        /// </summary>
        protected virtual void Clock_ClockTicked()
        {
            IsDataChanged = false;
        }

        /// <summary>
        /// Updates the value of the output buffer.
        /// </summary>
        /// <param name="value">The value of the output buffer.</param>
        protected override void UpdateValue(int value)
        {
            if (this.outputBuffer == null)
            {
                this.outputBuffer = new LimitedData(this.Name + ":OutputBuffer", value, this.NumberOfBits);
            }

            this.outputBuffer.IntValue = value;
        }
        #endregion
    }
}